// Package goawk is an implementation of AWK with CSV support
//
// You can use the command-line "goawk" command or run AWK from your
// Go programs using the "interp" package. The command-line program
// has the same interface as regular awk:
//
//	goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]
//
// The -F flag specifies the field separator (the default is to split
// on whitespace). The -v flag allows you to set a variable to a
// given value (multiple -v flags allowed). The -f flag allows you to
// read AWK source from a file instead of the 'prog' command-line
// argument. The rest of the arguments are input filenames (default
// is to read from stdin).
//
// A simple example (prints the sum of the numbers in the file's
// second column):
//
//	$ echo 'foo 12
//	> bar 34
//	> baz 56' >file.txt
//	$ goawk '{ sum += $2 } END { print sum }' file.txt
//	102
//
// To use GoAWK in your Go programs, see README.md or the "interp"
// package docs.
package main

import "C"
import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"unicode/utf8"

	"github.com/benhoyt/goawk/interp"
	"github.com/benhoyt/goawk/lexer"
)

const (
	version    = "v1.27.0"
	copyright  = "GoAWK " + version + " - Copyright (c) 2022 Ben Hoyt"
	shortUsage = "usage: goawk [-F fs] [-v var=value] [-f progfile | 'prog'] [file ...]"
	longUsage  = `Standard AWK arguments:
  --csv             enable CSV input mode; equivalent to "-i csv"
  -F separator      field separator (default " ")
  -f progfile       load AWK source from progfile (multiple allowed)
  -v var=value      variable assignment (multiple allowed)

Additional GoAWK features:
  -E progfile       load program, treat as last option, disable var=value args
  -H                parse header row and enable @"field" in CSV input mode
  -h, --help        show this help message
  -i mode           parse input into fields using CSV format (ignore FS and RS)
                    'csv|tsv [separator=<char>] [comment=<char>] [header]'
  -o mode           use CSV output for print with args (ignore OFS and ORS)
                    'csv|tsv [separator=<char>]'
  -version          show GoAWK version and exit

GoAWK debugging arguments:
  -coverappend      append to coverage profile instead of overwriting
  -covermode mode   set coverage mode: set, count (default "set")
  -coverprofile fn  write coverage profile to file
  -cpuprofile fn    write CPU profile to file
  -d                print parsed syntax tree to stdout and exit
  -da               print VM assembly instructions to stdout and exit
  -dt               print variable type information to stdout and exit
  -memprofile fn    write memory profile to file
`
)

func main() {

	{
		println("=========1")
		goRunNoReturn("", "BEGIN { print \"foo\",  42 }")
	}
	{
		println("\n=========1")
		goRunNoReturn("", "BEGIN { print \"foo\",  423 }")
	}
	//{
	//	println("=========1")
	//	s, _ := goRun("", "BEGIN { print \"foo\",  42 }")
	//	print(s)
	//}
	//{
	//	println("\n=========1")
	//	s, _ := goRun("", "BEGIN { print \"foo\",  423 }")
	//	print(s)
	//}
	//{
	//	println("\n=========2")
	//	src := "{ print NR, tolower($0) }"
	//	input := "A\naB\nAbC"
	//	print(goRun(input, src))
	//}
	//{
	//	println("\n==========3")
	//	src := "{ print $1 + $3 }"
	//	input := "6 1 2"
	//	print(goRunNoReturn(input, src))
	//}
	//{
	//	println("\n==========4")
	//	src := "$0 { print $1 }"
	//	input := "foo bar\n\nbaz buz"
	//	print(goRunNoReturn(input, src))
	//}
}

//export run
func run(msg *C.char, exp *C.char) *C.char {
	result, err := goRun(C.GoString(msg), C.GoString(exp))
	if err != nil {
		return C.CString("@SNC_ERROR@" + err.Error())
	}
	return C.CString(result)
}

func goRun(msg string, exp string) (string, error) {
	buf := new(bytes.Buffer)
	output := buf
	err := interp.Exec(exp, " ", strings.NewReader(msg), output)
	return buf.String(), err
}

func goRunNoReturn(msg string, exp string) error {
	return interp.Exec(exp, " ", strings.NewReader(msg), nil)
}

// Show source line and position of error, for example:
//
//	BEGIN { x*; }
//	          ^
func showSourceLine(src []byte, pos lexer.Position) {
	lines := bytes.Split(src, []byte{'\n'})
	srcLine := string(lines[pos.Line-1])
	numTabs := strings.Count(srcLine[:pos.Column-1], "\t")
	runeColumn := utf8.RuneCountInString(srcLine[:pos.Column-1])
	fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", "    ", -1))
	fmt.Fprintln(os.Stderr, strings.Repeat(" ", runeColumn)+strings.Repeat("   ", numTabs)+"^")
}

func errorExit(err error) {
	pathErr, ok := err.(*os.PathError)
	if ok && os.IsNotExist(err) {
		errorExitf("file %q not found", pathErr.Path)
	}
	errorExitf("%s", err)
}

func errorExitf(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}

func expandWildcardsOnWindows(args []string) []string {
	if runtime.GOOS != "windows" {
		return args
	}
	return expandWildcards(args)
}

// Originally from https://github.com/mattn/getwild (compatible LICENSE).
func expandWildcards(args []string) []string {
	result := make([]string, 0, len(args))
	for _, arg := range args {
		matches, err := filepath.Glob(arg)
		if err == nil && len(matches) > 0 {
			result = append(result, matches...)
		} else {
			result = append(result, arg)
		}
	}
	return result
}
